﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using HugeFlow.CommandPattern.Interactivity;
using HugeFlow.MVVM;
using System.ComponentModel;
//using System.Reflection.Emit;

namespace HugeFlow.CommandPattern
{
    /// <summary>
    /// Command pattern implementation
    /// </summary>
    public abstract class StaticCommand : ICommand
    {
        #region Static
        /// <summary>
        /// Static constructor. Initialize static properties
        /// </summary>
        static StaticCommand()
        {
            CommandCache = new Dictionary<string, StaticCommand>();
        }

        /// <summary>
        /// Gets the command cache.
        /// </summary>
        /// <value>The command cache.</value>
        public static Dictionary<string, StaticCommand> CommandCache
        {
            get;
            private set;
        }

        /// <summary>
        /// Finds the command.
        /// </summary>
        /// <param name="commandName">The command name.</param>
        /// <returns>returns the command for a given commandName</returns>
        public static StaticCommand FindCommand(string commandName)
        {
            if (string.IsNullOrEmpty(commandName))
            {
                return null;
            }

            // Check from cache
            StaticCommand cmd = null;
            if (CommandCache.TryGetValue(commandName, out cmd))
            {
                return cmd;
            }


            return null;
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the StaticCommand class.
        /// </summary>
        /// <param name="commandName">The command name used for retreiving command in Xaml.</param>
        protected StaticCommand(string commandName)
        {
            // Check from cache
            if (CommandCache.ContainsKey(commandName))
            {
                if (DesignerProperties.IsInDesignTool == false)
                    throw new ArgumentException(string.Format(CommandPatternResource.SameNameCommand, commandName));
                else
                    return;
            }
            
            CommandName = commandName;
            CommandCache.Add(commandName, this);
        }

        #endregion Constructors

        #region Events

        /// <summary>
        /// Check if the specified command can be executed.
        /// </summary>
        public event EventHandler CanExecuteChanged;


        #endregion Events

        #region Properties

        /// <summary>
        /// Gets the name of the command.
        /// </summary>
        public string CommandName
        {
            get;
            private set;
        }

        #endregion Properties

        #region Methods
        public abstract void Execute(object parameter);

        public abstract bool CanExecute(object parameter);
        #endregion
    }

    public class StaticCommand<T> : StaticCommand
    {
        Action<T> _executeAction;
        Func<T, bool> _canExecuteAction;


        #region Constructors
        public StaticCommand(string commandName,
                            Action<T> executeAction,
                            Func<T, bool> canExecuteAction)
            : base(commandName)
        {
            this._executeAction = executeAction;
            this._canExecuteAction = canExecuteAction;
        }

        public StaticCommand(string commandName,
                            Action<T> executeAction)
            : this(commandName, executeAction, null)
        {
        }
        #endregion

        public override bool CanExecute(object parameter)
        {
            if (_canExecuteAction == null)
                return true;

            return (_canExecuteAction((T)parameter));
        }

        public override void Execute(object parameter)
        {
            _executeAction((T)parameter);
        }
    }
}